package per.chenxin.util.tcp.http;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.codec.binary.Base64;

public class HttpUtil {
	final static int INT_BUFFER_SIZE = 8192;
	final static int INT_SO_TIMEOUT = 10000; // 10s

	public static void main(String[] strs) {

		if (strs.length != 2) {
			System.out.println("Usage: java " + HttpUtil.class.getName()
					+ " username password");
			System.exit(-1);
		}
		System.out.println(getProxyAuthorization(strs[0], strs[1]));
	}

	static public byte[] getBytesFromInputStream(Socket socket, String strURL)
			throws IOException {

		Pattern pattern = Pattern
				.compile("[a-zA-z]+://[^\\s]*[:[\\d]{0,5}]{0,1}[/]*.*");
		InputStream input = null;
		BufferedWriter output = null;

		Matcher matcher = pattern.matcher(strURL);
		String host = null;
		int port;
		String path;

		if (matcher.matches()) {
			int intStart = strURL.indexOf("://") + "://".length();
			int intEnd = strURL.indexOf("/", intStart);
			if (intEnd < 0) {
				intEnd = strURL.length();
			}
			host = strURL.substring(intStart, intEnd);
		} else
			return null;

		output = new BufferedWriter(new OutputStreamWriter(socket
				.getOutputStream()));

		output.write("GET " + strURL + " HTTP/1.1\r\n");
		output.write("Host: " + host + "\r\n");
		output.write("Connection: close\r\n");
		output.write("\r\n");
		output.flush();

		return getBytesFromInputStream(socket.getInputStream());

	}

	static public byte[] getBytesFromInputStream(InputStream input)
			throws IOException {

		StringBuffer sb = new StringBuffer();
		byte[] buffer = new byte[INT_BUFFER_SIZE];
		int intRead = 0;
		int intTotal = 0;
		List<byte[]> listBuffer = new LinkedList<byte[]>();

		while (intRead >= 0) {
			try {
				intRead = input.read(buffer, 0, INT_BUFFER_SIZE);
			} catch (SocketTimeoutException ste) {
				intRead = -1;
			}

			if (intRead > 0) {
				sb.append(new String(buffer, 0, intRead));
				intTotal += intRead;
				listBuffer.add(Arrays.copyOfRange(buffer, 0, intRead));
			}
		}

		byte[] bytes = new byte[intTotal];

		for (int i = 0, destPos = 0; i < listBuffer.size(); i++) {
			byte[] byteTemp = listBuffer.get(i);
			System.arraycopy(byteTemp, 0, bytes, destPos, byteTemp.length);
			destPos += byteTemp.length;
		}

		return bytes;

	}

	static public byte[] getMethod(String strUrl) {

		return getMethod(strUrl, null);
	}

	static public byte[] getMethod(String strUrl, ProxyEntity proxy) {
		Socket socket;
		try {
			socket = proxy != null ? getSocketFromProxy(proxy) : new Socket(
					(new URL(strUrl)).getHost(),
					(new URL(strUrl)).getPort() < 0 ? 80 : (new URL(strUrl))
							.getPort());
			socket.setSoTimeout(INT_SO_TIMEOUT);
			return getMethod(socket, strUrl, proxy.getProxyHost() != null,
					HttpUtil.getLastAuthorization(proxy));
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	static public String getProxyAuthorization(String proxyUser,
			String proxyPassword) {

		if (proxyUser != null && proxyPassword != null) {
			return new String(Base64
					.encodeBase64((proxyUser + ":" + proxyPassword).getBytes()));
		}
		return null;

	}

	static Socket getSocketFromProxy(ProxyEntity proxyEntity)
			throws UnknownHostException, IOException {
		if (proxyEntity.getProxyHost() == null
				|| proxyEntity.getProxyHost().trim().length() == 0)
			return null;
		Socket socket = new Socket(proxyEntity.getProxyHost(), proxyEntity
				.getProxyPort());
		String lastAuthorization = proxyEntity.getAuthorization();

		for (Entity entity : proxyEntity.listEntity) {
			HttpUtil.socketConnect(socket, entity.getProxyHost(), entity
					.getProxyPort(), lastAuthorization);
			lastAuthorization = entity.getAuthorization();
		}

		return socket;
	}

	static String getLastAuthorization(ProxyEntity proxyEntity) {
		String lastAuthorization = proxyEntity.getAuthorization();
		for (Entity entity : proxyEntity.listEntity) {
			lastAuthorization = entity.getAuthorization();
		}
		return lastAuthorization;
	}

	static void socketConnect(Socket connection, String host, int port,
			String username, String passoword) throws IOException {
		socketConnect(connection, host, port, getProxyAuthorization(username,
				passoword));
	}

	static void socketConnect(Socket connection, String host, int port)
			throws IOException {
		socketConnect(connection, host, port, null);
	}

	static void socketConnect(Socket connection, String host, int port,
			String strProxyAuthorization) throws IOException {
		Writer out = new OutputStreamWriter(connection.getOutputStream(),
				"ISO-8859-1");
		out.write("CONNECT " + host + ":" + port + " HTTP/1.0\r\n");
		// out.write("User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; IDCRL 4.100.313.1; IDCRL-cfg 4.0.5633.0; App msnmsgr.exe, 8.1.178.0, {7108E71A-9926-4FCB-BCC9-9A9D3F32E423})\r\n");
		out.write("Host: " + host + ":" + port + "\r\n");
		out.write("Content-Length: 0\r\n");
		out.write("Connection: Keep-Alive\r\n");
		out.write("Proxy-Connection: Keep-Alive\r\n");
		if (strProxyAuthorization != null) {
			out.write("Proxy-Authorization: Basic " + strProxyAuthorization
					+ "\r\n");
		}
		out.write("Pragma: no-cache\r\n");
		out.write("\r\n");
		out.flush();
		int oldSoTimeout = connection.getSoTimeout();
		connection.setSoTimeout(INT_SO_TIMEOUT);
		BufferedReader in = new BufferedReader(new InputStreamReader(connection
				.getInputStream(), "ISO-8859-1"));
		String line = null;
		if ((line = in.readLine()) == null
				|| !line.matches("^HTTP/[0-9][.][0-9] 2[0-9][0-9] .*")) {
			connection.close();
			throw new IOException(line);
		}
		//in.readLine();
		connection.setSoTimeout(oldSoTimeout);
	}

	static public byte[] getMethod(Socket socket, String strURL,
			boolean isProxy, String strProxyAuthorization) {

		URL url = null;
		InputStream input = null;
		BufferedWriter output = null;

		try {
			url = new URL(strURL);

			input = socket.getInputStream();
			output = new BufferedWriter(new OutputStreamWriter(socket
					.getOutputStream()));

			output.write("GET "
					+ (isProxy ? strURL : url.getFile().length() == 0 ? "/"
							: url.getFile()) + " HTTP/1.1\r\n");
			output.write("Host: " + url.getHost()
					+ (url.getPort() > 0 ? ":" + url.getPort() : "") + "\r\n");
			if (strProxyAuthorization != null
					&& strProxyAuthorization.trim().length() > 0) {
				output.write("Proxy-Authorization: Basic "
						+ strProxyAuthorization + "\r\n");
			}
			output.write("Connection: close\r\n");
			output.write("\r\n");
			output.flush();

			StringBuffer sb = new StringBuffer();
			byte[] buffer = new byte[INT_BUFFER_SIZE];
			int intRead = 0;
			int intTotal = 0;
			List<byte[]> listBuffer = new LinkedList<byte[]>();

			while (intRead >= 0) {
				while (input.available() < 0)
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
					}
				try {
					intRead = input.read(buffer, 0, INT_BUFFER_SIZE);
				} catch (java.net.SocketTimeoutException ste) {
					intRead = -1;
				}

				if (intRead > 0) {
					sb.append(new String(buffer, 0, intRead));
					intTotal += intRead;
					listBuffer.add(Arrays.copyOfRange(buffer, 0, intRead));
				}
			}

			byte[] bytes = new byte[intTotal];

			for (int i = 0, destPos = 0; i < listBuffer.size(); i++) {
				byte[] byteTemp = listBuffer.get(i);
				System.arraycopy(byteTemp, 0, bytes, destPos, byteTemp.length);
				destPos += byteTemp.length;
			}
			return bytes;
		} catch (NumberFormatException e) {
			//e.printStackTrace();
		} catch (IOException e) {
			//e.printStackTrace();
		} finally {
			if (input != null)
				try {
					input.close();
				} catch (IOException e) {
				}
			input = null;

			if (output != null)
				try {
					output.close();
				} catch (IOException e) {
				}
			output = null;

		}
		return null;

	}

	/**
	 * TODO: not completed
	 * 
	 * @param socket
	 * @param strURL
	 * @return
	 */
	static public byte[] getMethod(Socket socket, String strURL, boolean isProxy) {
		return getMethod(socket, strURL, isProxy, null);
	}

	static public void close(Closeable closeable) {

		if (closeable != null) {
			try {
				closeable.close();
			} catch (IOException e) {
			} finally {
				closeable = null;
			}
		}
	}
}
